C++ 小知识

用 cout 打印指针地址

如果是会自动输出内容的指针,转换为 void 指针即可

char* ptr = "fuck";
cout << "ptr address: " << (void*)ptr << endl;

利用 # ifdef 等编译指令切换调试模式

for 循环和 while 循环

可以相互转换,嵌套 for 循环其实也不需要真的嵌套,其实可以统一使用 for 循环,见下面的例子

#include <iostream>
#include <vector>
#include <Windows.h>

using namespace std;

void display(vector<int> vec);

#define __main main

class Solution
{
public:
    bool Find(vector<vector<int> > array, int target)
    {
        bool res = false;
        int row = array.size();
        int col = array[0].size();

        //  我们从右上角的元素找起来
        //  如果查找的元素比当前位置元素小, 就向左走
        //  如果查找的元素比当前位置元素大, 就向下走
        for (int i = 0, j = col - 1;
            (i >= 0 && i < row) && (j >= 0 && j < col);)
        {
            if (target == array[i][j])
            {
                res = true;
                break;
            }
            else if (target < array[i][j])     // 小的元素在当前位置左侧
            {
#ifdef __tmain
                display(array);
#endif // __tmain
                j--;
            }
            else
            {
#ifdef __tmain
                display(array);
#endif // __tmain
                i++;
            }
        }
        return res;
    }
};

void display(vector<int> vec)
{
    int len = vec.size();
    for (int i = 0; i<len; i++)
    {
        cout << vec[i] << ' ';
    }
    cout << endl;
}

int __main()
{
    int a1[] = { 1, 2, 8, 9, };
    int a2[] = { 2, 4, 9, 12, };
    int a3[] = { 4, 7, 10, 13, };
    int a4[] = { 6, 8, 11, 15, };
    vector<vector<int>> array;
    array.push_back(vector<int>(a1, a1 + 4));
    array.push_back(vector<int>(a2, a2 + 4));
    array.push_back(vector<int>(a3, a3 + 4));
    array.push_back(vector<int>(a4, a4 + 4));

    Solution solu;
    cout << solu.Find(array, 7) << endl;
    system("pause");
    return 0;
}

4 !的优先级

! 的优先级比 + - * / 等高

cout << !3-1 << endl << !(3-1);

//output -1(0-1)   0(!2)

5 迭代器的理解

拿 string 类举例来说,其

string str("abcdefg");
//删除第一个字符
str.erase(str.begin());
//删除第一个字符
str.erase(str.begin(),str.begin()+1);
//删除全部字符
str.erase(str.begin(),str.end());
//保留最后一个字符
str.erase(str.begin(),str.end()-1);

可以这样的理解,迭代器不同于普通的下标,而是一种类似于“隔板”一样的东西,str.begin() 返回的迭代器位于第一个字符前面,而 str.end() 返回的迭代器位于最后一个字符的后面,所以叫做”超尾迭代器“,如果成员函数的参数是首尾两个迭代器,那么删除的部分就位于这两个"隔板“之间。另外,string 类中的erase可以用int pos 作为参数,而其他的 STL 容器的 erase 好像只能用迭代器作为参数

6 NULL 和 nullptr

首先看一下 NULL 在 C 标准中的的定义

#ifndef NULL
    #ifdef __cplusplus
        #define NULL 0
    #else
        #define NULL ((void *)0)
    #endif
#endif

也就是说 NULL 在 C++ 中被 define 为 0. 而在 C 中 被 define 为 (void *)0。这在某些情况下是会出问题的

void f(int i){
cout << "take integer" << endl;
}
void f(int *p){
cout << "take pointer" << endl;
}

这两个重载函数,如果通过f(NULL)来调用 将调用第一个以 int 为参数的函数,而如果通过f(nullptr)来调用则会调用第二个函数 nullptr 是一个关键字 不存在上述问题

7 关于 cin 输入流

首先 关于 循环 while(cin) 的终止,似乎只能通过 EOF 也就是键盘上的 Ctrl + Z 来实现,如果你想对一段输入按空格拆分的时候如果是自己用键盘输入还好,可以输入 Ctrl + Z ,但是如果是测试题,那就没有机会输入 EOF 了。对于只有一行输入的情况,解决方法是

while(cin){
    cin >> str;
    if (cin.get()=='\n')
    break;
}

cin.get() 和 cin.getline() 的区别是 cin.get 不会丢弃换行符而是留在输入流里,而 cin.getline 会读取并丢弃换行符。

8 const 和指针

const 只要不和指针混在一起,那么其表达的意思是很清楚的,但是只要和指针混在一块,通常对于 const 到底是修饰指针本身还是指针所指向的对象就要费一番周折了。

实际上,判断 const 修饰的对象最简单的方法就是看 const 位于 * 的左边还是右边。如果 const 位于星号的左侧,则 const 就是用来修饰指针所指向的变量,即指针指向为常量;如果 const 位于星号的右侧,const 就是修饰指针本身,即指针本身是常量。

int b = 500;
const int* a = &b; //[1]
int const *a = &b; //[2]
int* const a = &b; //[3]
const int* const a = &b; //[4]

[1]和[2]的情况相同,都是指针所指向的内容为常量(const放在变量声明符的位置无关).[3]为指针本身是常量,而指针所指向的内容不是常量.[4]为指针本身和指向的内容均为常量。

关于解除引用运算符的优先级

++ 的优先级比 解除引用运算符 * 要强,但是 ++ 运算符必须等上一操作结束了才会执行

int * ptr = new int[8]{ 1,3,5,7,9,11,13,15 };
    int a = *ptr++;                                            //a=1,ptr->3
    cout << a << endl << *ptr++ << endl;                       //ptr->5
    int b = *(ptr++);                                          //b=5,ptr->7
    cout << b << endl << *(ptr++) << endl;                     //ptr->9
    int c = *ptr + 1;                                          //c=10,ptr->9
    cout << c << endl << *ptr + 1 << endl;                     //ptr->9
    int d = *(ptr + 1);                                        //d=11,ptr->9
    cout << d << endl << *(ptr + 1) << endl;

    // output: 1 3 5 7 10 10 11 11

总结,i++ 的优先级很高,但是必须保证在上一操作结束后执行这一点上有无括号并不影响,若是使用 ++i 结果又不一样。

C 中的内存对齐

从一个例子开始,下面这段代码,一样的结构体占用的空间大小却不一样。究其原因就是内存对齐。

内存对齐主要是因为

  • 有些机器无法访问任意位置的内存
  • 内存分块存取有利于提升效率
#include <iostream>

 using namespace std;

 struct st1
 {
    char a;
    int  b;
    short c;
};
 struct st2
 {
     short c;
     char  a;
     int   b;
 };

 int main()
 {
     cout << "sizeof(st1) is " << sizeof(st1) << endl;
     cout << "sizeof(st2) is " << sizeof(st2) << endl;
     system("pause");
     return 0;
 }

程序输出 分别为 12 8

内存对齐主要有两条原则

  1. 前面的地址(总和)必须是后面地址的整数倍
  2. 整个 struct 的地址必须是最大字节的整数倍

那么对于 st1 char(1) int(4),所以 char 后需添加 3 Byte,再然后 short(2) ,前面 int(4) 是其整数倍。

整个 st1 10 Byte,不是最大长度 int(4) 整数倍,故添加 3 Byte

对于 st2 第一个 short(2),第二个 char(1) 符合公式1,再然后的 int(4),那么前面必须额外添加 1 Byte 使前面有 4 Byte。

整个 st2 8 Byte 是最大长度 int(4) 的整数倍

函数声明有何意义呢,为什么不直接定义?

C/C++ 采用一种 分散编译的机制,把每个 .c 文件编译成一个二进制包然后再链接。这个机制是历史遗留产物,主要是由于当时的机器不足以维护整个项目的符号表。